Skip to content

feat: configurable injection of js and css - #174

Open
m-abs wants to merge 11 commits into
mainfrom
feat/injectable-js-css
Open

feat: configurable injection of js and css#174
m-abs wants to merge 11 commits into
mainfrom
feat/injectable-js-css

Conversation

@m-abs

@m-abs m-abs commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator

Restored dev branch from before rewritting history

@m-abs
m-abs marked this pull request as ready for review August 25, 2026 13:15

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a cross-platform API for registering additional JavaScript and CSS assets to be injected into EPUB HTML resources, extending the existing built-in flutterReadiumTools.js / flutterReadiumTools.css helper injection. It introduces a new shared InjectionAsset model and wires the method-channel plumbing, with Android implementing the injection into HTML resources and iOS adding storage + method handlers.

Changes:

  • Added InjectionAsset (JSON-serializable) and new platform-interface methods: setCssInjections / setJavaScriptInjections.
  • Android: implemented configurable injection by composing built-in helpers with extra injection assets and making injection idempotent via marker replacement.
  • iOS: added method-channel handlers and state for injection lists; updated docs/changelog and extended tooling scripts.

Reviewed changes

Copilot reviewed 13 out of 14 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
flutter_readium/test/flutter_readium_test.dart Updates mock platform to satisfy new platform interface methods.
flutter_readium/lib/flutter_readium.dart Exposes new public API methods on FlutterReadium.
flutter_readium/ios/flutter_readium/Sources/flutter_readium/FlutterReadiumPlugin.swift Adds iOS-side injection asset parsing and method-channel handlers; stores injection lists.
flutter_readium/CHANGELOG.md Documents the new injection feature and platform support.
flutter_readium/android/src/main/kotlin/dk/nota/flutterreadium/ReadiumReader.kt Passes configured injections into the HTML injection pipeline.
flutter_readium/android/src/main/kotlin/dk/nota/flutterreadium/ReadiumExtensions.kt Implements injection asset URL building and marker-based replace/insert logic.
flutter_readium/android/src/main/kotlin/dk/nota/flutterreadium/PublicationChannel.kt Adds method-channel handlers to set injections from Dart.
flutter_readium_platform_interface/lib/src/shared/injection_asset.dart Introduces the shared Dart InjectionAsset model and JSON serialization.
flutter_readium_platform_interface/lib/src/shared/index.dart Exports InjectionAsset from the shared model index.
flutter_readium_platform_interface/lib/method_channel_flutter_readium.dart Implements method-channel calls for the new injection setters.
flutter_readium_platform_interface/lib/flutter_readium_platform_interface.dart Adds the new injection methods to the platform interface contract.
CLAUDE.md Updates documented repo toolchain facts and Flutter version update guidance.
bin/format Extends formatting script behavior (formatting scope + added analysis calls).
.vscode/settings.json Adjusts tool auto-approve configuration entries.
Suppressed comments (2)

flutter_readium/ios/flutter_readium/Sources/flutter_readium/FlutterReadiumPlugin.swift:233

  • The arguments coming from Dart are bridged as [[String: Any]] (with NSNull for nulls). Casting to [[String: Any?]] will often fail and silently fall back to [], making injection registration a no-op on iOS. Also, mapping via the force-casting initializer can crash; use a compactMap with the failable initializer.
    case "setCssInjections":
      let items = call.arguments as? [[String: Any?]] ?? []
      self.cssInjections = items.map { InjectionAsset(from: $0) }
      result(nil)
    case "setJavaScriptInjections":

flutter_readium/ios/flutter_readium/Sources/flutter_readium/FlutterReadiumPlugin.swift:43

  • These new injection lists are stored and can be set over the method channel, but they are never read when building the WKWebView user scripts. As a result, the iOS implementation appears to accept injections without actually injecting them into EPUB resources.
  /// Extra CSS assets injected alongside the built-in helpers.
  var cssInjections: [InjectionAsset] = []

  /// Extra JavaScript assets injected alongside the built-in helpers.
  var javaScriptInjections: [InjectionAsset] = []

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread flutter_readium/lib/flutter_readium.dart Outdated
Comment on lines +5 to +17
/// Identifies a Flutter asset (JS or CSS file) to inject into EPUB HTML resources.
///
/// [assetPath] is the asset path as declared in `pubspec.yaml`, e.g. `assets/custom.js`.
/// [package] is the pub package that owns the asset, or `null` for app-level assets.
/// The file type is inferred from the path extension (`.js` or `.css`).
@immutable
class InjectionAsset implements JSONable {
const InjectionAsset({required this.assetPath, this.package});

factory InjectionAsset.fromJson(Map<String, dynamic> json) => InjectionAsset(
assetPath: json['assetPath'] as String,
package: json['package'] as String?,
);
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 14 changed files in this pull request and generated 3 comments.

Suppressed comments (3)

Previously missed (2) — in code that hasn't changed since the last review.

flutter_readium/android/src/main/kotlin/dk/nota/flutterreadium/PublicationChannel.kt:120

  • Indentation for the setAudioRecoveryPolicy branch is inconsistent with the rest of this when and is likely to fail ktlint formatting checks.
             "setAudioRecoveryPolicy" -> {
                 val args = arguments as? Map<*, *>
                 ReadiumReader.audioRecoveryPolicy = AudioRecoveryPolicy.fromMap(args)
                 return Try.success(null)
             }

flutter_readium/android/src/main/kotlin/dk/nota/flutterreadium/ReadiumExtensions.kt:215

  • <link> is a void element in HTML and should not be closed with </link>; this can break parsing in stricter EPUB/XHTML contexts. Prefer a self-closing tag.
                    injection.assetPath.endsWith(".css", ignoreCase = true) -> {
                        """<link rel="stylesheet" type="text/css" href="${injection.assetUrl}"></link>"""
                    }

flutter_readium/ios/flutter_readium/Sources/flutter_readium/FlutterReadiumPlugin.swift:237

  • Same issue as setCssInjections: InjectionAsset(from:) is failable so map yields optionals and won’t assign to [InjectionAsset]. Use compactMap and a non-optional dictionary cast.
    case "setJavaScriptInjections":
      let items = call.arguments as? [[String: Any?]] ?? []
      self.javaScriptInjections = items.map { InjectionAsset(from: $0) }
      result(nil)

Comment on lines +40 to +45
/// Extra CSS assets injected alongside the built-in helpers.
var cssInjections: [InjectionAsset] = []

/// Extra JavaScript assets injected alongside the built-in helpers.
var javaScriptInjections: [InjectionAsset] = []

Comment on lines +69 to +73
/// Registers extra CSS assets to inject into every EPUB HTML resource,
/// in addition to the built-in `flutterReadiumTools.css`.
/// Call before opening a publication so the injections are active when the reader view is created.
Future<void> setCssInjections(List<InjectionAsset> injections) =>
_readiumCall(() => _platform.setCssInjections(injections));

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 14 changed files in this pull request and generated 2 comments.

Suppressed comments (3)

flutter_readium/android/src/main/kotlin/dk/nota/flutterreadium/PublicationChannel.kt:144

  • Same issue as CSS injections: map["assetPath"] as String can throw and crash the plugin on malformed input. Prefer a safe cast and drop invalid items.
                        InjectionAsset(
                            assetPath = map["assetPath"] as String,
                            packageName = map["package"] as? String,
                        )
                    }

flutter_readium/ios/flutter_readium/Sources/flutter_readium/FlutterReadiumPlugin.swift:44

  • cssInjections / javaScriptInjections are only assigned (via method calls) and never read anywhere in this file, so iOS never actually injects the configured assets. This makes the Dart API and changelog claim (“supported on iOS”) incorrect until the injections are wired into the EPUB resource/HTML injection pipeline during publication open.
  /// Extra CSS assets injected alongside the built-in helpers.
  var cssInjections: [InjectionAsset] = []

  /// Extra JavaScript assets injected alongside the built-in helpers.
  var javaScriptInjections: [InjectionAsset] = []

flutter_readium_platform_interface/lib/src/shared/injection_asset.dart:25

  • There are extensive serialization round-trip tests in flutter_readium_platform_interface/test/models_test.dart, but this new model isn’t covered. Adding a small InjectionAsset toJson/fromJson round-trip test (and optionally a MethodChannel argument-shape test) would help prevent breaking the method-channel contract.
  @override
  Map<String, dynamic> toJson() => {}
    ..put('assetPath', assetPath)
    ..putOpt('package', package);

Comment on lines +125 to +131
ReadiumReader.cssInjections =
items.map { map ->
InjectionAsset(
assetPath = map["assetPath"] as String,
packageName = map["package"] as? String,
)
}
Comment on lines +243 to +245
if (endIdx == -1) {
PluginLog.w(TAG, "::injectScriptsAndStyles. Injection start marker found without end marker in: $filename")
} else {
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants